package com.flyqiu.flow.model.format.flowable;

import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import org.dom4j.Attribute;
import org.dom4j.Element;
import org.dom4j.Node;
import com.flyqiu.common.tools.StringTools;

import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

public class DefaultFlowableElementFormat {

    public static final String NAMESPACE_PREFIX_KEY = "namespacePrefix";
    public static final String ELEMENT_TYPE_KEY = "elementType";
    protected static final Set<String> IGNORE_KEYS = new HashSet<>();

    protected FlyQiuFlowFlowableBpmnFormat bpmnFormat;

    static {
        IGNORE_KEYS.add(NAMESPACE_PREFIX_KEY);
        IGNORE_KEYS.add(ELEMENT_TYPE_KEY);

    }

    public String xmlElementType() {
        return null;
    }

    public String jsonElementType() {
        return xmlElementType();
    }

    private void init(FlyQiuFlowFlowableBpmnFormat format) {
        this.bpmnFormat = format;
    }

    public final JSONObject elementToJson(FlyQiuFlowFlowableBpmnFormat flyQiuFlowFlowableBpmnFormat, Element element) {
        init(flyQiuFlowFlowableBpmnFormat);
        List<Attribute> attributes = element.attributes();
        Map<String, Attribute> elementAttrMap = attributes.stream().collect(Collectors.toMap(Node::getName, e -> e));
        JSONObject result = new JSONObject();
        String namespacePrefix = element.getNamespacePrefix();
        if (StringTools.isNotBlank(namespacePrefix)) {
            result.put(NAMESPACE_PREFIX_KEY, namespacePrefix);
        }
        String elementName = element.getName();

        result.put("elementType", elementName);
        handlerElementRemove("id", "defKey", attributes, elementAttrMap, result);
        handlerElementRemove("name", "defName", attributes, elementAttrMap, result);
        handlerElementRemove("default", "defaultTargetKey", attributes, elementAttrMap, result);

        if (StringTools.isNotBlank(element.getText()) && !IGNORE_KEYS.contains("#" + elementName)) {
            result.put("#" + elementName, element.getText());
        }
        for (Attribute attribute : attributes) {
            result.put(attribute.getName(), attribute.getValue());
        }
        elementToJson(element, attributes, elementAttrMap, result);
        return result;
    }

    protected void handlerElementRemove(String attarName, String mapperName, List<Attribute> attributes, Map<String, Attribute> elementAttrMap, JSONObject result) {
        if (!elementAttrMap.containsKey(attarName)) {
            return;
        }
        Attribute attribute = elementAttrMap.get(attarName);
        result.put(mapperName, attribute.getValue());
        attributes.remove(attribute);
    }

    protected void elementToJson(Element element, List<Attribute> unProcessedAttrs, Map<String, Attribute> elementAttrMap, JSONObject result) {
        List<Element> elements = element.elements();
        if (elements.isEmpty()) return;
        elementsToJson(elements, result);
    }

    protected void elementsToJson(List<Element> childElements, JSONObject result) {
        for (Element el : childElements) {
            String name = el.getName();
            if (IGNORE_KEYS.contains(name)) continue;
            DefaultFlowableElementFormat elementFormat = bpmnFormat.getElementFormat(name);
            JSONObject json = elementFormat.elementToJson(bpmnFormat, el);
            JSONArray array = result.getJSONArray(name);
            if (array == null) {
                array = new JSONArray();
                result.put(name, array);
            }
            array.add(json);
        }
    }


    public final Element jsonToElement(FlyQiuFlowFlowableBpmnFormat flyQiuFlowFlowableBpmnFormat, JSONObject jsonElement, Element parentElement) {
        Element srcElement = parentElement.addElement(getFullElementName(jsonElement));
        init(flyQiuFlowFlowableBpmnFormat);
        Set<String> keys = jsonElement.keySet();
        jsonAttrToElementAttr(jsonElement, keys, "defKey", srcElement, "id");
        jsonAttrToElementAttr(jsonElement, keys, "defName", srcElement, "name");
        jsonAttrToElementAttr(jsonElement, keys, "defaultTargetKey", srcElement, "default");
        jsonAttrToElementAttr(jsonElement, keys, "#" + srcElement.getName(), srcElement, null);
        jsonToElement(jsonElement, keys, srcElement);
        return  srcElement;
    }

    public String getElementType(JSONObject json) {
        return json.getString("elementType");
    }

    public String getFullElementName(JSONObject json) {
        String elementType = getElementType(json);
        if (!json.containsKey(NAMESPACE_PREFIX_KEY)) {
            return elementType;
        }
        return json.getString(NAMESPACE_PREFIX_KEY) + ":" + elementType;
    }

    protected void jsonToElement(JSONObject jsonElement, Set<String> unProcessedKeys, Element srcElement) {
        HashSet<String> set = new HashSet<>(unProcessedKeys);
        for (String key : set) {
            jsonAttrToElementAttr(jsonElement, unProcessedKeys, key, srcElement, key);
        }
    }

    public void jsonAttrToElementAttr(JSONObject json, Set<String> keys, String jsonKey, Element element, String elementAttarName) {
        if (IGNORE_KEYS.contains(jsonKey)) {
            keys.remove(jsonKey);
            return;
        }
        if (!json.containsKey(jsonKey)) {
            return;
        }
        if (elementAttarName == null) {
            elementAttarName = jsonKey;
        }
        Object value = json.get(jsonKey);
        if (value == null) {
            return;
        }
        if (jsonKey.startsWith("#")) {
            element.addText(json.getString(jsonKey));
            keys.remove(jsonKey);
            return;
        }
        if (value instanceof JSONObject) {
            jsonToElement(bpmnFormat, (JSONObject) value,element);


        } else if (value instanceof JSONArray attas) {
            for (int i = 0; i < attas.size(); i++) {
                JSONObject att = attas.getJSONObject(i);
                jsonToElement(bpmnFormat, att,element);
            }
        } else {
            element.addAttribute(elementAttarName, json.getString(jsonKey));
        }

        keys.remove(jsonKey);
    }

}
